package com.zzyl.base;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.Page;
import com.zzyl.utils.ConvertHandler;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

/**
 * 封装分页查询结果的通用类
 * @param <T>
 */
@Data
@ApiModel(value = "分页数据消息体", description = "分页数据统一对象")
// 使得创建 PageResponse 对象更加灵活和可读，可以使用链式调用的方式设置属性，避免了构造函数参数过多导致的混乱
@Builder
// 自动生成一个包含所有字段的私有构造函数。
// access = AccessLevel.PRIVATE: 限制了只能在类内部或通过静态方法调用此构造函数。
// 通常与 @Builder 配合使用，强制使用建造者模式来创建对象，避免直接使用构造函数，从而更好地控制对象创建的过程。
@AllArgsConstructor(access = AccessLevel.PRIVATE)
// 自动生成一个无参构造函数。
@NoArgsConstructor
public class PageResponse<T> {

    @ApiModelProperty(value = "总条目数", required = true)
    private Long total = 0L;

    @ApiModelProperty(value = "页尺寸", required = true)
    private Integer pageSize = 0;

    @ApiModelProperty(value = "总页数", required = true)
    private Long pages = 0L;

    @ApiModelProperty(value = "页码", required = true)
    private Integer page = 0;

    //  T 是一个泛型，意味着这个 PageResponse 可以封装任何类型的数据列表。
    @ApiModelProperty(value = "数据列表", required = true)
    private List<T> records = Collections.EMPTY_LIST; // 默认为空集合，避免 NPE


    /**
     * 通过mybatis的分页对象构造对象，不封装 items 属性
     *
     * @param page MyBatis 的 Page 对象
     */
    public PageResponse(Page<?> page) {
        this.page = Convert.toInt(page.getPageNum());
        this.total = page.getTotal();
        this.pageSize = Convert.toInt(page.getPageSize());
        this.pages = (long) page.getPages();
    }

    /**
     * 通过mybatis的分页对象构造对象，封装 items 属性
     *
     * @param page  MyBatis 的 Page 对象
     * @param clazz 指定 items 属性的类型
     */
    public PageResponse(Page<?> page, Class<T> clazz) {
        this.page = Convert.toInt(page.getPageNum());
        this.total = page.getTotal();
        this.pageSize = Convert.toInt(page.getPageSize());
        this.pages = (long) page.getPages();
    }

    /**
     * 返回一个分页对象实例
     *
     * @return 分页数据对象
     */
    public static <T> PageResponse<T> getInstance() {
        return PageResponse.<T>builder().build();
    }

    /**
     * Page{@link Page}对象封装为PageResponse,不封装 items 属性
     *
     * @param page 源分页对象
     * @return 目标分页数据对象
     */
    public static <T> PageResponse<T> of(Page<?> page) {
        //封装分页数据
        return PageResponse.<T>builder()
                .page(Convert.toInt(page.getPageNum()))
                .pageSize(Convert.toInt(page.getPageSize()))
                .pages((long) page.getPages())
                .total(page.getTotal())
                .build();
    }

    /**
     * Page{@link Page}对象封装为PageResponse，并将Page中的Records转换为指定类型封装为items
     *
     * @param page 源分页对象
     * @return 目标分页数据对象
     */
    public static <T> PageResponse<T> of(Page<?> page, Class<T> clazz) {
        return of(page, clazz, null);
    }

    /**
     * Page{@link Page}对象封装为PageResponse,
     * 并将Page中的Records转换为指定类型封装为items
     *
     * @param page           源分页对象
     * @param convertHandler 特殊对象类型转换器，可传null，即不进行特殊处理
     * @return 目标分页数据对象
     */
    public static <O, T> PageResponse<T> of(Page<O> page, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
        //封装分页数据
        return PageResponse.<T>builder()
            .page(Convert.toInt(page.getPageNum()))
            .pageSize(Convert.toInt(page.getPageSize()))
            .pages((long) page.getPages())
            .total(page.getTotal())
            .records(copyToList(page.getResult(), clazz, convertHandler))
            .build();
    }

    /**
     * 对items进行类型转换
     *
     * @param origin 源分页数据对象
     * @param clazz  指定items 属性的类型,不能为null
     * @return 目标分页数据对象
     */
    public static <O, T> PageResponse<T> of(PageResponse<O> origin, Class<T> clazz) {
        return of(origin, clazz, null);
    }

    /**
     * 对items进行类型转换
     *
     * @param origin         源分页数据对象
     * @param clazz          指定items 属性的类型,不能为null
     * @param convertHandler 特殊对象类型转换器，可传null，即不进行特殊处理
     * @return 目标分页数据对象
     */
    public static <O, T> PageResponse<T> of(PageResponse<O> origin, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
        //复制除items外的属性
        PageResponse<T> target = PageResponse.getInstance();
        BeanUtil.copyProperties(origin, target, "items");

        //items为空，直接返回
        if (CollUtil.isEmpty(origin.getRecords())) {
            return target;
        }
        List<T> targetList = copyToList(origin.getRecords(), clazz, convertHandler);
        target.setRecords(targetList);

        //封装分页数据
        return target;
    }

    /**
     * List{@link List}封装为分页数据对象
     *
     * @param items    item数据
     * @param page     页码,可不传,数据不为空时默认为1
     * @param pageSize 页尺寸,可不传,数据不为空时默认为1
     * @param pages    页尺寸,可不传,数据不为空时默认为1
     * @param counts   总条目数,可不传,数据不为空时默认为1
     * @return 目标分页数据对象
     */
    public static <T> PageResponse<T> of(List<T> items, Integer page, Integer pageSize, Long pages, Long counts) {
        //封装分页数据
        PageResponse<T> pageResponse = PageResponse.<T>builder()
                .page(Optional.ofNullable(page).orElse(1))
                .pageSize(Optional.ofNullable(pageSize).orElse(1))
                .pages(Optional.ofNullable(pages).orElse(1L))
                .total(Optional.ofNullable(counts).orElse(1L))
                .build();

        if (CollUtil.isEmpty(items)) {
            return pageResponse;
        }

        pageResponse.setRecords(items);
        return pageResponse;
    }

    /**
     * List{@link List}封装为分页数据对象
     * 数据不为空时，page、pageSize、pages、counts均默认为1
     *
     * @param items item数据
     * @return 目标分页数据对象
     */
    public static <T> PageResponse<T> of(List<T> items) {
        return of(items, null, null, null, null);
    }

    /**
     * 返回包含任意数量元素的分页对象
     * 数据不为空时，page、pageSize、pages、counts均默认为1
     *
     * @param elements items元素
     * @return 目标分页数据对象
     */
    @SafeVarargs
    public static <E> PageResponse<E> of(E... elements) {
        return of(List.of(elements));
    }

    /**
     * 对items进行类型转换
     *
     * @param origin  源分页数据对象
     * @param function 自定义函数
     * @return 目标分页数据对象
     */
    public static <O, T> PageResponse<T> of(PageResponse<O> origin, Function<List<O>, List<T>> function) {
        List<T> orderVOList = function.apply(origin.getRecords());
        return PageResponse.of(orderVOList, origin.getPage(), origin.getPageSize(), origin.getPages(), origin.total);
    }


    /**
     * 转换结构：将一个源列表（content）中的对象转换成另一个列表（targetList）中的对象。
     *
     * 基础转换通过 BeanUtil 实现，适合简单的字段映射。
     * 特殊转换通过 convertHandler 实现，适合复杂的字段处理或逻辑转换。
     *
     * @param content         原始列表，包含需要转换的对象（源数据列表）。
     * @param clazz           目标类型的 Class 对象，用于指定目标列表中元素的类型。
     * @param convertHandler  自定义转换处理器，用于处理从源类型到目标类型的特殊转换逻辑。
     * @param <T>             目标类型参数。
     * @param <O>             源类型参数。
     * @return                转换后的目标类型列表。
     */
    private static <T, O> List<T> copyToList(List<O> content, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
        // Step 1: 使用 BeanUtil 工具类将源列表 content 复制为目标类型的列表 targetList。
        // BeanUtil.copyToList 通常用于将一个列表中的对象转换为另一个列表中指定类型的对象。
        List<T> targetList = BeanUtil.copyToList(content, clazz);

        // Step 2: 检查目标列表是否非空，并且 convertHandler 是否存在。
        // 目标列表为空时，直接返回；convertHandler 为空时，跳过特殊转换逻辑。
        if (CollUtil.isNotEmpty(targetList) && ObjectUtil.isNotEmpty(convertHandler)) {
            // Step 3: 遍历源列表内容，对每一个元素调用自定义的 convertHandler 进行特殊转换。
            for (int i = 0; i < content.size(); i++) {
                // 调用 convertHandler.map 方法，将源列表的第 i 个元素和目标列表的第 i 个元素进行关联转换。
                convertHandler.map(content.get(i), targetList.get(i));
            }
        }

        // Step 4: 返回转换后的目标列表。
        return targetList;
    }

}
